--- title: "日期问题" created: 2025-11-28 tags: - 算法 --- # 日期问题 ## 分析 ### 格式化输入输出 用scanf读入 使用`scanf("%d/%d/%d", &a, &b, &c);` 在02/03/03这样的输入中提取到02 03 04这几个值 `scanf("%04d%02d%02d", &year, &month, &day);` 将8位数自动处理到年月日 用printf输出 `printf("%d-%02d-%02d\n", year, month, day);` 可以将9月输出为09 原理是输出占两位 不足用0填补 `sprintf(s,"%04d%02d%02d",y,m,d);` 将年月日读入到字符串里面去 直接可以对字符串进行处理 ### 筛日期 一个很常用的技巧 要什么日期 首先先把这段时间的合法的日期全都找出来 然后再对他们进行操作 类似于筛素数 先筛再操作 筛选也很简单 将日期用八位数表示 从起始到结束循环就行了 不用考虑多少进位的问题 多枚举那点不算什么 `for (int date = Start; date <= End; date ++ ){` `int year = date / 10000, month = date % 10000 / 100, day = date % 100;` `if (check_valid(year, month, day))` `……` `}` `}` 如何判断是否是合法日期呢 首先年份没什么可看的 基本没什么限制 月份要是0~12 天数就比较复杂 用一个数组记录每个月应该有的天数 `int days[13] = {0, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31};` 然后首先就是要在0~31的范围内 其次就是与某月应该有的天数进行比较 这里二月比较特殊 还要另外判断一个闰年 四年一闰:年份是4的倍数是闰年; 百年不闰:是100的倍数不是闰年; 四百年又闰:是400的倍数却又是闰年。 `if(year % 100 && year % 4 == 0 || year % 400 == 0)` `bool check_date(int year, int month, int day){` `if (month == 0 || month > 12)` `return false;` `if (day == 0 || day > 31)` `return false;` `int curdays=days[month];` `if(month==2)` `if(year % 100 && year % 4 == 0 || year % 400 == 0)` `curdays+=1;` `if(day>curdays)` `return false;` `return true;` `}` 这种方式 泛用性也更强了 ```cpp #include using namespace std; int days[13]={0,31,28,31,30,31,30,31,31,30,31,30,31}; bool check_date(int year,int month,int day){ if(month==0 || month>12) return false; if(day==0 || day>31) return false; int curdays=days[month]; if(month==2) if(year%100 && year%4==0 && year%400==0) curdays++; if(day>curdays) return false; return true; } int main() { int one,two,three; scanf("%d/%d/%d",&one,&two,&three); cin>>Start>>End; for(int date=Start;date<=End;date++){ int year=date/10000; //取前4位(砍掉后4位) / 4个0 int month=date%10000/100;//取后4位 %4个0 取后4位的前2位 再砍去2个0 int day=date%100; //取后2位 直接 %2个0 if(check_date(year,month,day)){ //具体逻辑 //………… printf("%d-%02d-%02d\n", year, month, day); } } return 0; } ``` ### 算相差天数 类似于前缀和的思想 ![[image-27614fe9.png]] 分别计算两个日期距离一个公元1年1月1日的总天数 然后计算这两个天数之间的差值来得到日期差 注意考虑闰年对2月天数的影响 ```cpp #include using namespace std; int days[13] = {0, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31}; bool isLeapYear(int year) { return (year % 4 == 0 && year % 100 != 0) || (year % 400 == 0); } // 计算从公元1年1月1日到指定日期的总天数 int daysFromStart(int year, int month, int day) { int totalDays = 0; // 添加之前年份的天数 for(int y = 1; y < year; y++) { totalDays += isLeapYear(y) ? 366 : 365; } // 添加当前年份的月份天数 for(int m = 1; m < month; m++) { totalDays += days[m]; if(m == 2 && isLeapYear(year)) { totalDays++; } } // 添加当前月份的天数 totalDays += day; return totalDays; } int main() { int startDate, endDate; while(cin >> startDate >> endDate) { if(startDate>endDate) swap(startDate,endDate); int startYear=startDate/10000, startMonth=startDate%10000/100, startDay=startDate%100; int endYear=endDate/10000, endMonth=endDate%10000/100, endDay=endDate%100; int dateDiff = daysFromStart(endYear,endMonth,endDay) - daysFromStart(startYear,startMonth,startDay) + 1; cout << dateDiff << endl; } return 0; } ``` ## \*组合拳 写日期问题 把这几个函数一默写上 后面就基本不成问题了 ```cpp int days[13] = {0, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31}; bool is_leap(int y){ return y%100 && y%4==0 || y%400==0; } int get_days(int y,int m){ return days[m]+(m==2 && is_leap(y)); } void next_day(int &y,int &m,int &d){//注意 传的是引用 d++; if(d>get_days(y,m)){ d=1; m++; if(m>12){ m=1; y++; } } } bool check_date(int y, int m, int d) { if (m < 1 || m > 12) return false; if(d < 1 || d > getdays(y,m)) return false; return true; } while ((curYear < 给定年份) || (curYear == 给定年份 && curMonth < 给定月份) || (curYear == 给定年份 && curMonth == 给定月份 && curDay < 给定日子)) { // 执行 next_day 操作 } ``` ## 题目 - [[2-Learning/02-算法/03-刷题理模型/暴力 枚举 模拟 问题/日期问题/日期问题|日期问题]] - [[日期差值|日期差值]] - [[回文日期|回文日期]] - [[回文日期2|回文日期2]] - [[日期计算|日期计算]] - [[航班时间|航班时间]] --- ⬅️ [[打酱油|打酱油]] 🏠 [[00-刷题理模型]] ➡️ [[回文日期|回文日期]]